<template>
  <div ref="editorRef"></div>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";

const props = defineProps({
  /** v-model */
  modelValue: String,
  /** 语言 */
  language: String,
  /** 主题 */
  theme: String,
  /** 原始内容 */
  original: String,
  /** 原始内容语言 */
  originalLanguage: String,
  /** 配置 */
  config: Object,
});

const emit = defineEmits(["update:modelValue", "update:original"]);

/** 编辑器实例 */
let editorIns = null;
/** 内容模型 */
let modifiedModel = null;
/** 原始内容模型 */
let originalModel = null;

/** 编辑器节点 */
const editorRef = ref(null);

/** 更新modelValue */
const updateModelValue = (value) => {
  emit("update:modelValue", value);
};

/** 更新original */
const updateOriginal = (original) => {
  emit("update:original", original);
};

/** 渲染编辑器 */
const render = () => {
  if (!editorRef.value) {
    return;
  }
  editorIns = monaco.editor.createDiffEditor(editorRef.value, {
    theme: props.theme,
    automaticLayout: true,
    ...(props.config || {}),
  });
  if (modifiedModel == null) {
    modifiedModel = monaco.editor.createModel(
      props.modelValue || "",
      props.language,
    );
    modifiedModel.onDidChangeContent(() => {
      updateModelValue(modifiedModel?.getValue?.());
    });
  }
  if (originalModel == null) {
    originalModel = monaco.editor.createModel(
      props.original || "",
      props.originalLanguage || props.language,
    );
    originalModel.onDidChangeContent(() => {
      updateOriginal(originalModel?.getValue?.());
    });
  }
  editorIns.setModel({
    original: originalModel,
    modified: modifiedModel,
  });
};

/** 销毁编辑器 */
const destory = () => {
  if (editorIns != null) {
    editorIns.dispose();
    editorIns = null;
  }
};

/** 销毁模型 */
const destoryModel = () => {
  if (modifiedModel != null) {
    modifiedModel.dispose();
    modifiedModel = null;
  }
  if (originalModel != null) {
    originalModel.dispose();
    originalModel = null;
  }
};

/** 获取编辑器实例 */
const getEditorIns = () => {
  return editorIns;
};

/** 更新代码语言和编辑器内容 */
watch(
  [
    () => props.modelValue,
    () => props.language,
    () => props.original,
    () => props.originalLanguage,
  ],
  () => {
    if (
      modifiedModel == null ||
      originalModel == null ||
      modifiedModel.getLanguageId() !== props.language ||
      originalModel.getLanguageId() !== props.originalLanguage
    ) {
      destoryModel();
      destory();
      nextTick(() => {
        render();
      });
      return;
    }
    const content = props.modelValue || "";
    if (content !== modifiedModel.getValue()) {
      modifiedModel.setValue(content);
    }
    const originalContent = props.original || "";
    if (originalContent !== originalModel.getValue()) {
      originalModel.setValue(originalContent);
    }
  },
);

/** 更新主题和配置 */
watch(
  [() => props.theme, () => props.config],
  () => {
    destory();
    nextTick(() => {
      render();
    });
  },
  { deep: true },
);

onMounted(() => {
  render();
});

onBeforeUnmount(() => {
  destory();
});

defineExpose({ getEditorIns });
</script>
